/*
 *  +----------------------------------------------------------------------
 *  | sophon [ A FAST GAME FRAMEWORK ]
 *  +----------------------------------------------------------------------
 *  | Copyright (c) 2023-2029 All rights reserved.
 *  +----------------------------------------------------------------------
 *  | Licensed ( http:www.apache.org/licenses/LICENSE-2.0 )
 *  +----------------------------------------------------------------------
 *  | Author: jqiris <1920624985@qq.com>
 *  +----------------------------------------------------------------------
 */

use anyhow::Result;
use once_cell::sync::OnceCell;
use prost::Message;
use redis::{self, ToRedisArgs};
use serde::{de::DeserializeOwned, Serialize};

pub use impl_redis::*;
use redlock::Lock;

use crate::fatal;
use crate::{configs::StoreConf, logger::*};

mod impl_redis;
mod redlock;

static mut DEF_STO: OnceCell<StoreRedis> = OnceCell::new();

pub fn init_store(cfg: StoreConf) {
    if &cfg.use_type != "redis" {
        fatal!("not support store type: {}", &cfg.use_type);
        return;
    }
    match StoreRedis::new(cfg) {
        Ok(sto) => unsafe {
            if let Err(_) = DEF_STO.set(sto) {
                fatal!("init_store err");
            }
        },
        Err(err) => {
            fatal!("init_store err:{}", err);
        }
    }
}

//global function
pub fn global_store() -> &'static mut StoreRedis {
    unsafe { DEF_STO.get_mut().expect("store is not initialized") }
}

pub fn get_key(key: &str) -> String {
    let store = global_store();
    store.get_key(key)
}

pub fn get_keys(keys: Vec<String>) -> Vec<String> {
    let store = global_store();
    store.get_keys(keys)
}

pub fn set<T: Serialize + DeserializeOwned>(key: &str, value: T, seconds: usize) -> Result<()> {
    let store = global_store();
    store.set(key, value, seconds)
}

pub fn set_nx<T: Serialize + DeserializeOwned>(key: &str, value: T) -> Result<()> {
    let store = global_store();
    store.set_nx(key, value)
}

pub fn set_proto<T: Message + Default>(key: &str, value: T, seconds: usize) -> Result<()> {
    let store = global_store();
    store.set_proto(key, value, seconds)
}

pub fn get<T: Serialize + DeserializeOwned>(key: &str) -> Result<T> {
    let store = global_store();
    store.get(key)
}

pub fn get_proto<T: Message + Default>(key: &str) -> Result<T> {
    let store = global_store();
    store.get_proto(key)
}

pub fn del(key: &str) -> Result<()> {
    let store = global_store();
    store.del(key)
}

pub fn del_keys(keys: Vec<String>) -> Result<()> {
    let store = global_store();
    store.del_keys(keys)
}

pub fn del_pattern(pattern: &str) -> Result<()> {
    let store = global_store();
    store.del_pattern(pattern)
}

pub fn exists(key: &str) -> bool {
    let store = global_store();
    store.exists(key)
}

pub fn hset<T: Serialize + DeserializeOwned>(key: &str, field: String, value: T) -> Result<()> {
    let store = global_store();
    store.hset(key, field, value)
}

pub fn hset_nx<T: Serialize + DeserializeOwned>(key: &str, field: String, value: T) -> Result<()> {
    let store = global_store();
    store.hset_nx(key, field, value)
}

pub fn hget<T: Serialize + DeserializeOwned>(key: &str, field: String) -> Result<T> {
    let store = global_store();
    store.hget(key, field)
}

pub fn hset_proto<T: Message + Default>(key: &str, field: String, value: T) -> Result<()> {
    let store = global_store();
    store.hset_proto(key, field, value)
}

pub fn hset_proto_nx<T: Message + Default>(key: &str, field: String, value: T) -> Result<()> {
    let store = global_store();
    store.hset_proto_nx(key, field, value)
}

pub fn hget_proto<T: Message + Default>(key: &str, field: String) -> Result<T> {
    let store = global_store();
    store.hget_proto(key, field)
}

pub fn hdel<F: ToRedisArgs>(key: &str, fields: F) -> Result<()> {
    let store = global_store();
    store.hdel(key, fields)
}

pub fn hexists(key: &str, field: String) -> bool {
    let store = global_store();
    store.hexists(key, field)
}

pub fn hincr<D: ToRedisArgs>(key: &str, field: String, incr: D) -> Result<()> {
    let store = global_store();
    store.hincr(key, field, incr)
}

pub fn lpush<T: Serialize + DeserializeOwned>(key: &str, value: T) -> Result<()> {
    let store = global_store();
    store.lpush(key, value)
}

pub fn lpop<T: Serialize + DeserializeOwned>(key: &str) -> Result<T> {
    let store = global_store();
    store.lpop(key)
}

pub fn lpop_proto<T: Message + Default>(key: &str) -> Result<T> {
    let store = global_store();
    store.lpop_proto(key)
}

pub fn blpop<T: Serialize + DeserializeOwned>(key: &str) -> Result<T> {
    let store = global_store();
    store.blpop(key)
}

pub fn blpop_proto<T: Message + Default>(key: &str) -> Result<T> {
    let store = global_store();
    store.blpop_proto(key)
}

pub fn rpush<T: Serialize + DeserializeOwned>(key: &str, value: T) -> Result<()> {
    let store = global_store();
    store.rpush(key, value)
}

pub fn rpop<T: Serialize + DeserializeOwned>(key: &str) -> Result<T> {
    let store = global_store();
    store.rpop(key)
}

pub fn rpop_proto<T: Message + Default>(key: &str) -> Result<T> {
    let store = global_store();
    store.rpop_proto(key)
}

pub fn brpop<T: Serialize + DeserializeOwned>(key: &str) -> Result<T> {
    let store = global_store();
    store.brpop(key)
}

pub fn brpop_proto<T: Message + Default>(key: &str) -> Result<T> {
    let store = global_store();
    store.brpop_proto(key)
}
pub fn scard(key: &str) -> Result<i64> {
    let store = global_store();
    store.scard(key)
}

pub fn sadd<T: Serialize + DeserializeOwned>(key: &str, value: T) -> Result<()> {
    let store = global_store();
    store.sadd(key, value)
}

pub fn sadd_proto<T: Message + Default>(key: &str, value: T) -> Result<()> {
    let store = global_store();
    store.sadd_proto(key, value)
}

pub fn spop<T: Serialize + DeserializeOwned>(key: &str) -> Result<T> {
    let store = global_store();
    store.spop(key)
}

pub fn spop_proto<T: Message + Default>(key: &str) -> Result<T> {
    let store = global_store();
    store.spop_proto(key)
}

pub fn spop_n<T: Serialize + DeserializeOwned>(key: &str, num: i64) -> Result<Vec<T>> {
    let store = global_store();
    store.spop_n(key, num)
}

pub fn spop_n_proto<T: Message + Default>(key: &str, num: i64) -> Result<Vec<T>> {
    let store = global_store();
    store.spop_n_proto(key, num)
}

pub fn lock(resource: &str, ttl: usize) -> Result<Lock> {
    let store = global_store();
    store.lock(resource, ttl)
}

pub fn unlock(lock: &Lock) {
    let store = global_store();
    store.unlock(lock)
}
